package com.mo.xi.ge.info.loc

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.*
import androidx.annotation.RequiresPermission
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.location.*
import java.util.*

/**
 * data:2024/01/25
 */
internal class LocationManager(private val context: Context) {

    private var mFusedLocationClient: FusedLocationProviderClient? = null
    private var mLocationCallback: LocationCallback? = null
    private var mLocationRequest: LocationRequest? = null
    private var lastTime: Long = 0

    //    private var mServiceHandler: Handler? = null
//    private var mTimeoutHandler: Handler? = null
//    private var mHandlerThread: HandlerThread? = null
    private var mLastLocation: Location? = null


    //receive all callbacks
    private val listenerLocationSync: Vector<(Location?) -> Unit> = Vector()

    //only receive your own callback, may be replaced by other's callback
    private val listenerOnceLocationSync: Vector<(Location?) -> Unit> = Vector()

    private var mLocationManager: LocationManager? = null
    private var mLocationListener: LocationListener? = null
    private var isOnceOnly = true


    @SuppressLint("MissingPermission")
    fun startLocation() {
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
        mFusedLocationClient?.setMockMode(false)
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                if (locationResult.lastLocation != null) {
                    onNewLocation(locationResult.lastLocation)
                } else {
                    onEmptyLocation()

                }
            }

            override fun onLocationAvailability(loca: LocationAvailability) {
                super.onLocationAvailability(loca)
                val bLocationAvailability = loca.isLocationAvailable

                if (!bLocationAvailability) {//定位不可用就系统api定位
                    apiLocation()
                }
            }
        }

//        mServiceHandler = Handler(mHandlerThread!!.looper)
        createLocationRequest()
//        mTimeoutHandler = TimeoutHandler(mHandlerThread?.looper ?: return)
    }

    /**
     * LocationManager
     */
    @SuppressLint("MissingPermission")
    private fun apiLocation() {
        try {
//            Log.d(TAG, "api locationManager: start")
            mLocationListener?.also {
                mLocationManager?.removeUpdates(it)
            }
            if (mLocationManager == null) {
                mLocationManager =
                    context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            }

            mLocationListener = object : LocationListener {
                override fun onLocationChanged(location: Location) {
//                    Log.d(TAG, "api locationManager: $location")
                    onNewLocation(location)
                }

                override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
//                    Log.d(TAG, "api locationManager: onStatusChanged $provider $status")
                }

                override fun onProviderEnabled(provider: String) {
//                    Log.d(TAG, "api locationManager: Enabled $provider ")
                }

                override fun onProviderDisabled(provider: String) {
//                    Log.d(TAG, "api locationManager: Disabled $provider ")
                }
            }

            mLocationManager!!.getLastKnownLocation(LocationManager.NETWORK_PROVIDER).let {
                if (it != null) {
                    onNewLocation(it)
                }

                val providerList = mLocationManager?.getProviders(true)
                if (providerList != null) {
                    for (provider in providerList) { if (LocationManager.NETWORK_PROVIDER == provider) {
                            mLocationManager?.requestLocationUpdates(
                                LocationManager.NETWORK_PROVIDER,
                                TIME_UPDATE_INTERVAL,
                                1F,
                                mLocationListener ?: return@let
                            )
                        } else if (LocationManager.PASSIVE_PROVIDER == provider) {
                            mLocationManager?.requestLocationUpdates(
                                LocationManager.PASSIVE_PROVIDER,
                                TIME_UPDATE_INTERVAL,
                                1F,
                                mLocationListener ?: return@let
                            )
                        }
                    }
                }
            }
        } catch (e: Exception) {
            onEmptyLocation()
        }
    }

    @Synchronized
    private fun onNewLocation(location: Location?) {
        location?.let { CacheLocation.updateCache(it) }
        dispatchLocationChanged(location)
        if (isOnceOnly && location != null) {
            removeLocationUpdates()
            mLastLocation = location
//            mTimeoutHandler?.removeMessages(TIMEOUT_MESSAGE)
        }
    }

    private fun dispatchLocationChanged(location: Location?) {
        listenerLocationSync.forEach { callback ->
            callback.invoke(location)
        }
        listenerOnceLocationSync.forEach { callback ->
            callback.invoke(location)
        }
        removeAllOnceListener()
    }

    private fun isProviderEnable(): Boolean {
        return try {
            val locationMang = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            locationMang.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        } catch (e: Exception) {
            false
        }
    }

    fun requestUpdate() {
        try {
            if (!isProviderEnable()) {
                onEmptyLocation()
                return
            }
            lastTime = System.currentTimeMillis()

            removeLocationUpdates()

            createLocationRequest()

            if (GoogleApiAvailability.getInstance()
                    .isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
            ) {
                val callbc = mLocationCallback
                if (callbc != null) {
                    mLocationRequest?.let {
                        mFusedLocationClient?.requestLocationUpdates(
                            it, callbc, Looper.myLooper()
                        )
                    }
                }
            } else {
                apiLocation()
            }

        } catch (unlikely: SecurityException) {
//            Log.e(TAG, "Lost location permission. Could not request updates. $unlikely")
        } catch (except: Exception) {
//            Log.e(TAG, "requestLocationUpdates exception:$except")
        }
    }

    fun reqLocationOne(callback: (Location?) -> Unit) {
        isOnceOnly = true
        removeAllOnceListener()
        listenerOnceLocationSync.add(callback)
        requestUpdate()
    }

    fun reqLocation(callback: (Location?) -> Unit) {
        isOnceOnly = false
        listenerLocationSync.add(callback)
        requestUpdate()
    }

    fun getReqLastLocation(): Location? {
        return mLastLocation
    }

    private fun removeAllListener() {
        listenerLocationSync.clear()
        removeAllOnceListener()
    }

    private fun removeAllOnceListener() {
        listenerOnceLocationSync.clear()
    }

    private fun removeLocationUpdates() {
        try {
            mLocationCallback?.let {
                mFusedLocationClient?.removeLocationUpdates(it)
            }
            mLocationListener?.also {
                mLocationManager?.removeUpdates(it)
            }

        } catch (unlikely: SecurityException) {
//            Log.e(TAG, "Lost location permission. Could not remove updates. $unlikely")
        } catch (e: Exception) {
//            Log.e(TAG, e.message.toString())
        }
    }

    private fun createLocationRequest() {
        mLocationRequest = LocationRequest.create().apply {
            interval = TIME_UPDATE_INTERVAL
            fastestInterval = TIME_FASTEST_UPDATE_INTERVAL
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
    }

    fun stopLocation() {
        removeLocationUpdates()
        removeAllListener()
//        mHandlerThread?.quit()
//        mTimeoutHandler?.removeMessages(TIMEOUT_MESSAGE)
        mFusedLocationClient = null
        mLocationManager = null
    }


    private fun onEmptyLocation() {
        onNewLocation(mLastLocation)
    }

    @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
    fun getLastOneInfo(): Location? {
        val locaMang =
            context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        reqLocation { }
        val netProvider = locaMang.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        var lcoation: Location? = null
        if (netProvider) {
            lcoation = locaMang.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
        }
        return lcoation
    }


    companion object {
        const val TAG = "debug_LocationUpdatesService"
        const val TIME_UPDATE_INTERVAL: Long = 5000L
        const val TIME_FASTEST_UPDATE_INTERVAL = TIME_UPDATE_INTERVAL / 2
    }
}

